package com.njxxted.activiti.modules.process.task.handler;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.njxxted.activiti.common.annotation.StrategyHandleType;
import com.njxxted.activiti.common.constant.Constant;
import com.njxxted.activiti.common.util.StringUtils;
import com.njxxted.activiti.common.util.WorkflowUtils;
import com.njxxted.activiti.modules.process.cmd.TaskHandleCmd;
import com.njxxted.activiti.modules.process.defintion.dao.ActDefinitionDao;
import com.njxxted.activiti.modules.process.defintion.dao.ActRunDefinitionDao;
import com.njxxted.activiti.modules.process.defintion.entity.ActNodeConfigEntity;
import com.njxxted.activiti.modules.process.defintion.entity.ActRunNodeConfigEntity;
import com.njxxted.activiti.modules.process.extend.dao.ExtendActCopyDao;
import com.njxxted.activiti.modules.process.extend.entity.ExtendActCopyEntity;
import com.njxxted.activiti.modules.process.extend.entity.ExtendActInstanceEntity;
import com.njxxted.activiti.modules.process.extend.service.ExtendActInstanceService;
import org.activiti.bpmn.model.EndEvent;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.GroupQuery;
import org.activiti.engine.identity.User;
import org.activiti.engine.task.Task;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;

/**
 * 描述：任务审批同意处理器（策略模式）
 * <p>
 * 作者：Ostrich Hu
 * 时间：2019/9/29 9:42 星期日
 */
@Component
@StrategyHandleType(Constant.Flow.TASK_AGREE)
public class ActTaskAgreeHandler extends AbstractTaskStrategyHandler {

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public void taskHandle(TaskHandleCmd taskCmd) {
        Map<String, Object> elMap = taskCmd.getVariables();
        logger.debug("审批传递的流程变量：{}", elMap);
        //获取下个节点信息
        List<FlowNode> nextNodes = WorkflowUtils.getNextNode(taskCmd.getProcessDefId(), taskCmd.getCurrNodeKey(), elMap);
        taskService.claim(taskCmd.getCurrNodeId(), taskCmd.getOperateUserId());
        taskService.addComment(taskCmd.getCurrNodeId(), taskCmd.getInstanceId(), taskCmd.getComment());
        nextNodes.forEach(nextNode -> {
            if (nextNode instanceof EndEvent) {
                taskService.complete(taskCmd.getCurrNodeId());
                setExtendActEntity(Constant.Flow.TASK_FINISHED, taskCmd, taskCmd.getCurrNodeId(), taskCmd.getCurrNodeId(), "已结束", null);
            } else {
                Task nextTask = setNextTaskInfo(elMap, nextNode.getId(), taskCmd);
                setExtendActEntity(Constant.Flow.TASK_AGREE, taskCmd, taskCmd.getCurrNodeId(), nextTask.getId(), nextTask.getName(), nextTask);
            }
        });
    }

    /**
     * 设置下个节点信息
     *
     * @param elMap
     * @param nextNodeId
     * @param taskCmd
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public Task setNextTaskInfo(Map<String, Object> elMap, String nextNodeId, TaskHandleCmd taskCmd) {
        logger.debug("流程变量：{}", elMap);
        taskService.complete(taskCmd.getCurrNodeId(), elMap);
        Task task = taskService.createTaskQuery().taskDefinitionKey(nextNodeId)
                .processInstanceId(taskCmd.getInstanceId()).orderByTaskCreateTime().asc().list().get(0);

        //根据流程key和nodeId查询出配置的人员信息
        ActNodeConfigEntity nodeConfigEntity = new ActNodeConfigEntity();
        nodeConfigEntity.setKey(taskCmd.getKey());
        nodeConfigEntity.setNodeId(nextNodeId);
        List<ActNodeConfigEntity> definitionSettings = actDefinitionDao.getProcessDefinitionSettings(nodeConfigEntity);

        //判断节点在审批时是否设置了候选人和抄送人,如果设置了则使用审批时设置的人员，否则使用流程配置的默认人员信息
        List<Map> nextCandidateInfos = taskCmd.getNextCandidateInfos();
        List<Map> nextCopyInfos = taskCmd.getNextCopyInfos();
        //合并候选人和抄送人的集合
        List<Map> runConfigs = Lists.newArrayList();
        //设置下个节点候选人()
        if (nextCandidateInfos != null) {
            runConfigs.addAll(nextCandidateInfos);
        } else {
            runConfigs.addAll(matchedUsers(definitionSettings, Constant.Flow.ASSIGN_USER, nextNodeId, taskCmd.getInstanceId()));
        }
        //设置下个节点的抄送人
        CopyOnWriteArrayList<ExtendActCopyEntity> copyUserList = new CopyOnWriteArrayList<>();
        if (nextCopyInfos != null) {
            runConfigs.addAll(nextCopyInfos);
            nextCopyInfos.parallelStream().forEachOrdered(nextCopyInfo -> {
                ExtendActCopyEntity extendActCopyEntity = new ExtendActCopyEntity();
                extendActCopyEntity.setIsView("0");
                extendActCopyEntity.setInstanceId(taskCmd.getInstanceId());
                extendActCopyEntity.setTaskId(task.getId());
                extendActCopyEntity.setId(StringUtils.uuidGenerator());
                extendActCopyEntity.setUserId((String) nextCopyInfo.get("id"));
                extendActCopyEntity.setAddDate(new Date());
                copyUserList.add(extendActCopyEntity);
            });
        } else {
            Set<Map<String, String>> copyUsers = matchedUsers(definitionSettings, Constant.Flow.COPY_USER, nextNodeId, taskCmd.getInstanceId());
            runConfigs.addAll(copyUsers);
            copyUsers.parallelStream().forEachOrdered(u -> {
                ExtendActCopyEntity extendActCopyEntity = new ExtendActCopyEntity();
                extendActCopyEntity.setIsView("0");
                extendActCopyEntity.setInstanceId(taskCmd.getInstanceId());
                extendActCopyEntity.setTaskId(task.getId());
                extendActCopyEntity.setId(StringUtils.uuidGenerator());
                extendActCopyEntity.setUserId(u.get("id"));
                extendActCopyEntity.setAddDate(new Date());
                copyUserList.add(extendActCopyEntity);
            });
        }
        //保存节点抄送人
        if (copyUserList.size() > 0) {
            extendActCopyDao.saveBatch(copyUserList);
        }

        CopyOnWriteArrayList<ActRunNodeConfigEntity> assignUserList = new CopyOnWriteArrayList<>();
        runConfigs.parallelStream().forEachOrdered(conf -> {
            // 再向配置表中的节点候选人信息
            ActRunNodeConfigEntity new_config = new ActRunNodeConfigEntity();
            new_config.setAddDate(new Date());
            new_config.setKey(taskCmd.getKey());
            new_config.setInstanceId(taskCmd.getInstanceId());
            new_config.setNodeId(task.getTaskDefinitionKey());
            new_config.setRunTaskId(task.getId());
            new_config.setConfigType(conf.get("type").toString());
            new_config.setConfigValue((String) conf.get("fullId"));
            new_config.setOtherValue(conf.get("logic").toString());
            assignUserList.add(new_config);
        });
        actRunDefinitionDao.insertBatch(assignUserList);
        //根据流程实例id查询流程实例信息
        ExtendActInstanceEntity instanceEntity = extActPInstanceService.queryObject(taskCmd.getInstanceId());
        User startedUser = identityService.createUserQuery().userId(instanceEntity.getStartUserId()).singleResult();
        //查询流程运行时的配置信息，用于推送消息
        Map<String, Object> conditionMap = Maps.newHashMap();
        conditionMap.put("instanceName", instanceEntity.getName());
        conditionMap.put("startedUser", startedUser.getLastName());
        conditionMap.put("currNodeName", taskCmd.getCurrNodeName());
        //推送消息
        pushNotice(taskCmd.getInstanceId(), taskCmd.getKey(), instanceEntity.getName(), task.getTaskDefinitionKey(), conditionMap);
        return task;
    }

    /**
     * 匹配符合条件用户
     *
     * @param definitionSettings
     * @return
     */
    private Set<Map<String, String>> matchedUsers(List<ActNodeConfigEntity> definitionSettings,
                                                  String type, String nextNodeId, String instanceId) {
        Set<Map<String, String>> matchedUsers = new CopyOnWriteArraySet<>();
        //查询出流程发起人以及他所在的部门
        HistoricProcessInstance instance = historyService.createHistoricProcessInstanceQuery()
                .unfinished().processInstanceId(instanceId).singleResult();
        String startUserId = instance.getStartUserId();
        List<Group> departs = identityService.createGroupQuery().groupType("depart").groupMember(startUserId).list();
        String departIds = departs.parallelStream().map(Group::getId).collect(Collectors.joining(","));
        //过滤出符合下一节点的配置信息
        List<ActNodeConfigEntity> configs = definitionSettings.parallelStream()
                .filter(conf -> nextNodeId.equals(conf.getNodeId())).collect(Collectors.toList());
        // 匹配出符合条件的人员信息
        convertUser(type).parallelStream().forEachOrdered(user -> {
            StringBuilder stringBuilder = new StringBuilder();
            configs.parallelStream().forEachOrdered(setting -> {
                String otherValue = setting.getOtherValue();
                String[] arr = setting.getConfigValue().split("/");
                //候选人
                if ("assign".equals(type)) {
                    switch (setting.getConfigType()) {
                        case Constant.Flow.ASSIGN_USER:
                            stringBuilder.append(arr[arr.length - 1].equals(user.get("id"))).append(otherValue);
                            break;
                        case Constant.Flow.ASSIGN_DEPART:
                            stringBuilder.append(user.get("departId").contains(arr[arr.length - 1])).append(otherValue);
                            break;
                        case Constant.Flow.ASSIGN_ROLE:
                            stringBuilder.append(user.get("roleId").contains(arr[arr.length - 1])).append(otherValue);
                            break;
                        case Constant.Flow.ASSIGN_SPONSOR_DEPARTMENT:
                            stringBuilder.append(user.get("departId").contains(departIds)).append(otherValue);
                            break;
                    }
                } else {
                    //抄送人
                    switch (setting.getConfigType()) {
                        case Constant.Flow.COPY_USER:
                            stringBuilder.append(arr[arr.length - 1].equals(user.get("id"))).append(otherValue);
                            break;
                        case Constant.Flow.COPY_DEPART:
                            stringBuilder.append(user.get("departId").contains(arr[arr.length - 1])).append(otherValue);
                            break;
                        case Constant.Flow.COPY_ROLE:
                            stringBuilder.append(user.get("roleId").contains(arr[arr.length - 1])).append(otherValue);
                            break;
                        case Constant.Flow.COPY_SPONSOR_DEPARTMENT:
                            stringBuilder.append(user.get("departId").contains(departIds)).append(otherValue);
                            break;
                    }
                }

            });
            String conditionStr = stringBuilder.toString();
            String replace = conditionStr.replace("and", " && ").replace("or", " || ");
            if (StringUtils.isTrue(replace)) {
                matchedUsers.add(user);
            }
        });
        return matchedUsers;
    }

    /**
     * 转换 user为 map
     *
     * @return
     */
    private List<Map<String, String>> convertUser(String type) {
        List<Map<String, String>> res = new CopyOnWriteArrayList<>();
        //查询出所有人员信息
        List<User> allUserList = identityService.createUserQuery().list();
        allUserList.parallelStream().forEachOrdered(user -> {
            Map<String, String> map = Maps.newHashMap();
            List<Group> roleList = identityService.createGroupQuery().groupMember(user.getId()).groupType("role").list();
            List<Group> departList = identityService.createGroupQuery().groupMember(user.getId()).groupType("depart").list();
            String departIds = departList.parallelStream().map(Group::getId).collect(Collectors.joining(","));
            String roleIds = roleList.parallelStream().map(Group::getId).collect(Collectors.joining(","));
            map.put("id", user.getId());
            map.put("roleId", roleIds);
            map.put("departId", departIds);
            map.put("name", user.getLastName());
            map.put("type", type);
            map.put("logic", "or");
            map.put("fullId", "/" + user.getId());
            res.add(map);
        });
        return res;
    }
}
